#include "QueryEvaluator.h"

QueryEvaluator::QueryEvaluator()
{


	queryPreprocessor.createEntityTable();
	queryPreprocessor.createEntityRelTable();

	struct resultTypes tempResultStruct;
	vector<vector<int>> tempResult;
	tempResultStruct.resultEntity = tempResult;
	for(int i =0;i<11;i++)
	{
		result.resultVector.push_back(tempResultStruct);
	}


};


int QueryEvaluator::executeQuery(list<string>& resultVector)
{
	deleteAllRows(result);
	bool allIndependent = true;
	result.noOfRows = 0;
	clearWithMapping();
	vector<int> tempResultVector1;
	vector<int> tempResultVector2;
	vector<Result> withResult;
	int resultIndex =  0;
	int relationshipIndex = 0;
	vector<int> argumentsIndex;
	bool resultExists = true;
	Mock query;
	resultIndex = result.selectedType;
	int resultValue = 0;
	int j =0 ;
	unsigned int i =0;
	if(with.size()!=0)
	{
		while(i<with.size())
		{

			
			if(with[i].withType==_VALUE)
			{
				if((with[i].arguments[0].argument==result.selectedType) &&( with[i].arguments[0].argumentInd==result.resultIndex))
				{
					if(with[i].arguments[0].argument==_PROCEDURE)
					{
					resultVector.push_back(query.getEntityName(with[i].arguments[1].argument,_PROCEDURE));
					return 1;
					}
					else
					{
						resultVector.push_back(query.getEntityName(with[i].arguments[1].argument,_VARIABLE));
						return 1;
					
					}
	

				}
				withMapping[with[i].arguments[0].argument].clear();
				withMapping[with[i].arguments[0].argument].push_back(with[i].arguments[0].argumentInd);
				withMapping[with[i].arguments[0].argument].push_back(with[i].arguments[1].argument);

			}
			i++;
		}
	}
	if(suchThat.size()!=0)
	{
		while(j<suchThat.size())
		{

			if(suchThat[j].index!=1)
			{
				allIndependent = false;
			}
			relationshipIndex = suchThat[j].relationshipType;
			if(with.size()>0)
			{
				int argType1 = getWithMapping(suchThat[j].arguments[0].argument,suchThat[j].arguments[0].argumentInd);
				int argType2 = getWithMapping(suchThat[j].arguments[1].argument,suchThat[j].arguments[1].argumentInd);
				if(argType1>=0)
				{
					suchThat[j].arguments[0].argumentType = argType1;
				}
				if(argType2>=0)
				{
					suchThat[j].arguments[1].argumentType = argType2;
				}
			}
			if(relationshipIndex==0)
			{
				resultValue = query.getFollow(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==1)
			{
				resultValue = query.getFollowT(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==2)
			{
				resultValue = query.getModify(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==3)
			{
				resultValue = query.getUses(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==4)
			{
				resultValue = query.getParent(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==5)
			{
				resultValue = query.getParentT(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==6)
			{
				resultValue = query.getCalls(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}
			if(relationshipIndex==7)
			{
				resultValue = query.getCallsT(tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1]);
			}

			 if((suchThat[j].arguments[0].argumentType!=5)&&(suchThat[j].arguments[1].argumentType!=5))
			{
			if(result.selectedType==_BOOLEAN)
			{
				if(resultValue==-1)
				{
				resultExists = false;
				break;
				}
			
			}
			else
			{
			if(resultValue==-1)
				{
				return 1;
				}
			}
			}
			else if(suchThat[j].arguments[0].argumentType!=5)
			{
				if(tempResultVector2.size()<=0)
				{
					if(result.selectedType==_BOOLEAN)
					{
						resultExists = false;
						break;
					}
					return 1;
				}
			intersectResult(result,tempResultVector2,tempResultVector1,suchThat[j].arguments[1],suchThat[j].arguments[0],0);
			}
			else if(suchThat[j].arguments[1].argumentType!=5)
			{
				if(tempResultVector1.size()<=0)
				{
					if(result.selectedType==_BOOLEAN)
					{
						resultExists = false;
						break;
					}
					return 1;
				}
			intersectResult(result,tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1],0);
			}
			else
			{
				if((tempResultVector2.size()<=0)||(tempResultVector1.size()<=0))
				{
					if(result.selectedType==_BOOLEAN)
					{
						resultExists = false;
						break;
					}
					return 1;
				}
			intersectResult(result,tempResultVector1,tempResultVector2,suchThat[j].arguments[0],suchThat[j].arguments[1],1);
			
			}
			tempResultVector1.clear();
			tempResultVector2.clear();
			j++;
		}

	}
	
	
	if(with.size()==0 && suchThat.size()==0)
	{
		if(result.selectedType!=_BOOLEAN)
		{
			query.getAll(resultVector,result.selectedType);
		}


	}
	else
	{
		if(result.selectedType==_BOOLEAN)
		{
			if(resultExists)
			{
				resultVector.push_back("true");
			}
			else
			{	
				resultVector.push_back("false");
			}
		}
		else
		{
			if(allIndependent==true)
			{
				
				query.getAll(resultVector,result.selectedType);
			}
			else
			{
			getEntityMapping(result,resultVector);
			}
		}
	}
	return 1;

};

int QueryEvaluator::validateQuery(string query)
{


	int validationResult = queryPreprocessor.createQueryTree(query,suchThat,result,with);
	return validationResult;




	};

	void QueryEvaluator::getEntityMapping(struct Result& tempResult,list<string>& resultVector)
	{
		string tempName;
	Mock queryObj;
		if(tempResult.selectedType==_PROCEDURE||tempResult.selectedType==_VARIABLE)
	{
		
		for(int i =0;i<tempResult.noOfRows;i++)
		{
			resultVector.push_back(queryObj.getEntityName(tempResult.resultVector[tempResult.selectedType].resultEntity[tempResult.resultIndex][i],tempResult.selectedType));
		}

	}
	else
	{
		for(int i =0;i<tempResult.noOfRows;i++)
		{
			char temp[50];
			sprintf(temp,"%d",tempResult.resultVector[tempResult.selectedType].resultEntity[tempResult.resultIndex][i]);
			resultVector.push_back(temp);
		}

	}
}
int QueryEvaluator::clearWithMapping()
{
	vector<int> init;
	init.push_back(-1);
	init.push_back(-1);

	for(int k=0;k<11;k++)
	{
		withMapping.push_back(init);
	}
	return 1;
};
void QueryEvaluator::generateMapping(int variableType,vector<string>& variable)
{

	queryPreprocessor.createMapping(variableType,variable,result);

};

int QueryEvaluator::getWithMapping(int &arg, int argInd)
{
	int resultType = -1;
	if(arg==_STMT)
	{
		resultType =INTARGUMENT;
	}
	if(arg==_PROCEDURE)
	{
		resultType = PROCARGUMENT;
	}
	if(arg==_VARIABLE)
	{
		resultType = VARARGUMENT;
	}

	for(int q=0;q<withMapping[arg].size();q++)
	{
		if(argInd==withMapping[arg][q])
		{
			arg = withMapping[arg][q+1];
			return resultType;
		}
		q++;
	}



	return -1;




};
int QueryEvaluator::intersectResult(struct Result& result3, vector<int>& result1, vector<int>& result2, struct Argument& argument1, struct Argument& argument2, int both)
{
	int argumentTempInd = 0;//argument of dependent column
	int argumentTemp = 0;//argumentInd of dependent column


	int empty = checkEmpty(result3);
	//case 1: No result in result set. 
	if(empty==1)
	{
		for(int i = 0;i<result1.size();i++)
		{
			result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd].push_back(result1[i]);
			
			if(both==1)
			{
				result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd].push_back(result2[i]);
			}

			result3.noOfRows++;
		}
	}
	else
	{


		//check argument dependence
		int dependenceValue = checkIndependence(result3,argument1,argument2,both);
		if(dependenceValue==1)
		{
			if(result1.size()==0)
			{
				deleteAllRows(result3);
				return 1;
			}
			argumentTempInd = argument1.argumentInd;
			argumentTemp = argument1.argument;
		}
		if(dependenceValue==2)
		{
			if(result2.size()==0)
			{
				deleteAllRows(result3);
				return 1;
			}
			argumentTempInd = argument2.argumentInd;
			argumentTemp = argument2.argument;
		}
		//case 2: Independent from all the collumns 
		int resultVectorIndex = 0;
		int argIndex = 0;
		int tempNoOfRows = 0;
		tempNoOfRows++;

		int rowMax = result3.noOfRows;
		int count = 0;
		if(dependenceValue==-1)
		{

			for(int k = 0;k<result1.size();k++)
			{

				for(int p = 0;p<rowMax;p++)
				{
					for(int i=0;i< result3.resultVector.size();i++)
					{
						int temp = result3.resultVector[i].resultEntity.size();
						for(int j=0;j<temp;j++)
						{
							if(((i!=argument1.argument)||(i==argument1.argument&&j!=argument1.argumentInd))&&((both==0)||((i!=argument2.argument)||(i==argument2.argument&&j!=argument2.argumentInd))))
							{
								

								if(count>0)
								{
									if(result3.resultVector[i].resultEntity[j].size()>0)
									{
									int value = result3.resultVector[i].resultEntity[j][p];
									result3.resultVector[i].resultEntity[j].push_back(value);
									result3.noOfRows++;
									}

								}
							}
						}

					}
					result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd].push_back(result1[k]);
					if(both==1)
					{
						result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd].push_back(result2[k]);
					}
				}

				count++;


			}
		}
		else 
		{
			if(dependenceValue==1||dependenceValue==2)
			{
				//case 3: one column dependent from all the collumns 
				count = 0; //check if first iteration of insertions
				int noOfInsertions = 0;
				bool flag = false;
				for(int p = 0;p<rowMax;p++)
				{
					int value = result3.resultVector[argumentTemp].resultEntity[argumentTempInd][noOfInsertions];
					count = 0;
					for(int k = 0;k<result1.size();k++)
					{
						if((value==result1[k]&&dependenceValue==1)||((dependenceValue==2)&&(value==result2[k])))
						{
							flag = true;
							if(count>0)
							{
								for(int i=0;i< result3.resultVector.size();i++)
								{
									for(int j=0;j<result3.resultVector[i].resultEntity.size();j++)
									{
										if(result3.resultVector[i].resultEntity[j].size()!=0)
										{
											if((!((i==argument1.argument) &&( j== argument1.argumentInd)))&&(!((i==argument2.argument) && (j==argument2.argumentInd))))
											{
												int tempValue = result3.resultVector[i].resultEntity[j][noOfInsertions-1];
												result3.resultVector[i].resultEntity[j].insert(result3.resultVector[i].resultEntity[j].begin() + noOfInsertions,tempValue);
												
											}
										}
									}

								}
								if(dependenceValue==1)
								{
									int commonValue = result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd][noOfInsertions-1];
									result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd].insert(result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd].begin() + noOfInsertions,commonValue);
									result3.noOfRows++;
									if(both==1)
									{
										result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd].push_back(result2[k]);
									}
								}
								else
								{
									int commonValue = result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd][noOfInsertions-1];
									result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd].insert(result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd].begin() + noOfInsertions,commonValue);

									result3.noOfRows++;
									result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd].push_back(result1[k]);
								}
								noOfInsertions++;
							}
							else
							{
								//result3.resultVector[argument1.argumentType].resultEntity[argument1.argumentInd].push_back(result1[k]);

								if(dependenceValue==1)
								{
									if(both==1)
										result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd].push_back(result2[k]);

								}
								else
								{
									result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd].push_back(result1[k]);
								}
								count++;
								noOfInsertions++;
							}
						}
					}
					if(flag == false)
					{
						deleteRow(result3,noOfInsertions);
					}
					else
					{	
						flag = false;
					}
				}
			}
			else
				{
					bool flag= false;
					int noOfDeletions = 0;
				for(int p = 0;p<rowMax;p++)
				{
					int valueArg1 = result3.resultVector[argument1.argument].resultEntity[argument1.argumentInd][noOfDeletions];
					int valueArg2 = result3.resultVector[argument2.argument].resultEntity[argument2.argumentInd][noOfDeletions];
					for(int k = 0;k<result1.size();k++)
					{
						
						if((valueArg1==result1[k])&&(valueArg2==result2[k]))
						{
							flag = true;
						}
							
					}
					if(flag == false)
					{
						deleteRow(result3,noOfDeletions);
					}
					else
					{	noOfDeletions++;
						flag = false;
					}
				}
			}
		}
	}
	return 1;
}
int QueryEvaluator::deleteRow(struct Result& tempResult, int rowIndex)
{
	for(int i=0;i< tempResult.resultVector.size();i++)
	{
		for(int j=0;j<tempResult.resultVector[i].resultEntity.size();j++)
		{
			if(tempResult.resultVector[i].resultEntity[j].size()>rowIndex)
			{
				tempResult.resultVector[i].resultEntity[j].erase(tempResult.resultVector[i].resultEntity[j].begin() + rowIndex);
				
			}

		}
	}
	tempResult.noOfRows--;
	return 1;
}
int QueryEvaluator::deleteAllRows(struct Result& tempResult)
{
	for(int i=0;i< tempResult.resultVector.size();i++)
	{
		for(int j=0;j<tempResult.resultVector[i].resultEntity.size();j++)
		{

			tempResult.resultVector[i].resultEntity[j].clear();
			

		}
	}
	tempResult.noOfRows = 0;
	return 1;
}
int QueryEvaluator::checkIndependence(struct Result& tempResult, struct Argument& argument1,struct Argument& argument2, int both)
{
	int returnValue = -1;

	for(int i=0;i< tempResult.resultVector.size();i++)
	{
		for(int j=0;j<tempResult.resultVector[i].resultEntity.size();j++)
		{
			if(tempResult.resultVector[argument1.argument].resultEntity[argument1.argumentInd].size()!=0)
			{
				if(returnValue==2)
				{
					returnValue = 3;
					break;
				}
				else
				{
					returnValue = 1;
				}
			}
			if(both==1)
			{
				if(tempResult.resultVector[argument2.argument].resultEntity[argument2.argumentInd].size()!=0)
				{
					if(returnValue==1)
					{
						returnValue = 3;
						break;
					}
					else
					{
						returnValue = 2;
					}
				}
			}
		}
	}
	return returnValue;

}
int QueryEvaluator::checkEmpty(struct Result& tempResult)
{
	int empty =1;
	for(int i =0;i<tempResult.resultVector.size();i++)
	{
		for(int j =0;j<tempResult.resultVector[i].resultEntity.size();j++)
		{
			if(tempResult.resultVector[i].resultEntity[j].size()>0)
			{
				empty = 0;
				break;
			}
		}
	}
	return empty;
}






	/*
	void main()
	{
	vector<string> result;
	vector<string> s;
	s.push_back("s1");
	s.push_back("s");
	vector<string> v;
	v.push_back("v");
	vector<string> d;
	d.push_back("c");
	QueryEvaluator obj;
	obj.generateMapping(_STMT,s);
	obj.generateMapping(_VARIABLE,v);
	obj.generateMapping(_CONSTANT,d);
	int tmpeTest = obj.validateQuery("Select s such that Follows (2, s)");

	obj.executeQuery(result);
	}*/